package client

import (
	"sync"
	"sync/atomic"
	"time"
	transCommon "ws-demo-go/internal/transport/common"
	"ws-demo-go/pkg/logger"
	"ws-demo-go/pkg/ws/common"
)

const (
	sendPingInterval  = 10 * time.Second // 客户端发送 Ping 帧的间隔
	checkPongInterval = 5 * time.Second  // 检查 Pong 帧的间隔
	pongTimeout       = 15 * time.Second // 等待 Pong 帧的超时阈值，若超过此时间未收到服务端 Pong 帧，则关闭连接
	sendChaneSize     = 200              // 发送缓冲队列的大小
)

type connection struct {
	wsConn       *common.Conn         // 底层 WebSocket 连接
	sendChan     transCommon.SendChan // 发送消息缓冲队列
	onMessage    func(data []byte)    // 收到服务端消息的回调（交给上层处理）
	lastPongTime int64                // 服务端最后一次心跳的时间戳
	closeChan    chan struct{}        // 连接关闭通知通道
	closed       bool                 // 连接是否关闭
	mu           sync.Mutex           // 保护 closed 状态
}

func newConnection(wsConn *common.Conn) *connection {
	return &connection{
		wsConn:       wsConn,
		sendChan:     make(transCommon.SendChan, sendChaneSize),
		closeChan:    make(chan struct{}),
		lastPongTime: time.Now().Unix(),
	}
}

// 读循环：接收服务端消息，并通过 OnMessage 回调给上层
func (tc *connection) readLoop() {
	for {
		frame, data, err := (*tc.wsConn).ReadFrame2Bytes(true)
		if err != nil {
			if !common.IsClosedErr(err) {
				logger.Error("Read error: %v", err)
			}
			tc.close()
			return
		}
		switch frame.Opcode {
		case common.OpClose:
			// 如果是关闭帧，TODO 之后需要处理关闭帧

		case common.OpPong:
			// 如果是 Pong 帧（客户端只支持接收 Pong 帧），则刷新服务端连接的最后心跳时间
			atomic.StoreInt64(&tc.lastPongTime, time.Now().Unix())

		case common.OpBinary:
			// 如果是数据帧，将消息传递给上层
			if onMessage := tc.onMessage; onMessage != nil {
				onMessage(data)
			}

		default:
			logger.Error("Unknown opcode in client: %v", frame.Opcode)
		}
	}
}

// 写循环：发送消息到服务端
func (tc *connection) writeLoop() {
	for frame := range tc.sendChan {
		if err := (*tc.wsConn).WriteBytes2Frame(frame.Opcode, frame.Payload, true); err != nil {
			logger.Error("Write error: %v", err)
			tc.close()
			return
		}
	}
}

// 发送 Ping 帧循环
func (tc *connection) sendPingLoop() {
	ticker := time.NewTicker(sendPingInterval)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			err := transCommon.AddToSendChanWithoutBlocking(tc.sendChan, transCommon.PingFrame)
			if err != nil {
				logger.Error("Write to send chan error: %v", err)
			}
		case <-tc.closeChan:
			return
		}
	}
}

// 检测 Pong 帧循环
func (tc *connection) checkPongLoop() {
	ticker := time.NewTicker(checkPongInterval)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			if tc.isPongTimeout() {
				logger.Info("Server pong timeout, close the connection")
				tc.close()
				return
			}
		case <-tc.closeChan:
			return
		}
	}
}

// 发送消息到服务端
func (tc *connection) sendBytes(data []byte) error {
	return transCommon.SendBytes(tc.sendChan, data)
}

// 检查连接是否关闭
func (tc *connection) isClosed() bool {
	tc.mu.Lock()
	defer tc.mu.Unlock()
	return tc.closed
}

// 检查 Pong 帧是否超时
func (tc *connection) isPongTimeout() bool {
	lastPong := time.Unix(atomic.LoadInt64(&tc.lastPongTime), 0)
	return time.Since(lastPong) > pongTimeout
}

// 关闭连接
func (tc *connection) close() {
	tc.mu.Lock()
	defer tc.mu.Unlock()
	if tc.closed {
		return
	}

	err := (*tc.wsConn).Close()
	if err != nil {
		logger.Error("close error: %v", err)
		return
	}
	close(tc.sendChan)

	tc.closed = true
	close(tc.closeChan)
}
